import 'dart:math';

import 'package:flutter/material.dart';
import 'bezier_util.dart';

class InteractiveGestureBezierDemo extends StatefulWidget {
  InteractiveGestureBezierDemo({Key? key}) : super(key: key);

  @override
  State<InteractiveGestureBezierDemo> createState() =>
      _InteractiveGestureBezierDemoState();
}

class _InteractiveGestureBezierDemoState
    extends State<InteractiveGestureBezierDemo> {
  var points = <Offset>[];
  var finished = false;
  var indexOfPointToMove = -1;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Listener(
        onPointerUp: ((event) {
          print('1: ${event.localPosition}');
          if (!finished) {
            points.add(event.localPosition);
            setState(() {});
          } else {
            indexOfPointToMove = -1;
          }
        }),
        onPointerDown: ((event) {
          print('1: ${event.localPosition}');
          if (finished) {
            int index = checkPointToMove(event.localPosition);
            if (index != -1) {
              indexOfPointToMove = index;
            }
          }
        }),
        onPointerSignal: ((event) {
          print(event.toString());
        }),
        onPointerCancel: ((event) {
          print('2:${event.localPosition}');
        }),
        onPointerMove: ((event) {
          print('move: ${event.original?.localPosition}');
          if (indexOfPointToMove != -1) {
            points[indexOfPointToMove] = event.localPosition;
            setState(() {});
          }
        }),
        behavior: HitTestBehavior.opaque,
        child: CustomPaint(
          foregroundPainter: GestureBezierPainter(points: points),
          child: Container(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            color: Color(0xFFF5F5F5),
          ),
        ),
      ),
      bottomSheet: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          TextButton(
            onPressed: () {
              if (!finished) {
                if (points.isNotEmpty) {
                  points.removeLast();
                  setState(() {});
                }
              } else {
                points.clear();
                finished = false;
                setState(() {});
              }
            },
            child: Text(finished ? '重新绘制' : '撤销'),
          ),
          SizedBox(
            width: 20,
          ),
          TextButton(
            onPressed: () {
              if (points.isNotEmpty) {
                finished = true;
                setState(() {});
              }
            },
            child: Text('完成绘制'),
          ),
        ],
      ),
    );
  }

  int checkPointToMove(Offset pressedPoint) {
    if (points.isNotEmpty) {
      var pointsToCheck = <Offset>[];
      final maxDistance = 10.0;
      for (Offset p in points) {
        if ((p.dx - pressedPoint.dx).abs() < maxDistance &&
            (p.dy - pressedPoint.dy).abs() < maxDistance) {
          pointsToCheck.add(p);
        }
      }

      if (pointsToCheck.length == 0) {
        return -1;
      } else if (pointsToCheck.length == 1) {
        return points.indexOf(pointsToCheck[0]);
      } else {
        Offset point = pointsToCheck[0];
        var distance = distanceBetween(pointsToCheck[0], pressedPoint);
        for (int i = 1; i < pointsToCheck.length; i++) {
          var newDistance = distanceBetween(pointsToCheck[i], pressedPoint);
          if (newDistance < distance) {
            point = pointsToCheck[i];
            distance = newDistance;
          }
        }

        return points.indexOf(point);
      }
    }

    return -1;
  }

  double distanceBetween(Offset p1, Offset p2) {
    return sqrt(
        (p1.dx - p2.dx) * (p1.dx - p2.dx) + (p1.dy - p2.dy) * (p1.dy - p2.dy));
  }
}

class GestureBezierPainter extends CustomPainter {
  GestureBezierPainter({required this.points});
  final List<Offset> points;
  @override
  void paint(Canvas canvas, Size size) {
    print(size);
    canvas.drawColor(Color(0xFFF1F1F1), BlendMode.color);
    var paint = Paint()..color = Color(0xFFE53020);
    paint.strokeWidth = 2.0;
    paint.style = PaintingStyle.stroke;
    for (var point in points) {
      canvas.drawCircle(point, 2.0, paint);
    }
    // 拆分
    paint.color = Color(0xFF24D090);
    drawCurves(canvas, paint, points);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

  void drawCurves(Canvas canvas, Paint paint, List<Offset> points) {
    if (points.length <= 1) {
      return;
    }
    if (points.length == 2) {
      canvas.drawLine(points[0], points[1], paint);
      return;
    }
    if (points.length == 3) {
      _draw2OrderBezierCurves(canvas, paint, points);
      return;
    }
    if (points.length == 4) {
      _draw3OrderBezierCurves(canvas, paint, points);
      return;
    }
    var subPoints = points.sublist(0, 4);
    drawCurves(canvas, paint, subPoints);
    drawCurves(canvas, paint, points.sublist(3));
  }

  _draw3OrderBezierCurves(Canvas canvas, Paint paint, List<Offset> points) {
    assert(points.length == 4);
    var yGap = 60.0;
    var path = Path();
    path.moveTo(points[0].dx, points[0].dy);
    for (var t = 1; t <= 100; t += 1) {
      var curvePoint = BezierUtil.get3OrderBezierPoint(
          points[0], points[1], points[2], points[3], t / 100.0);

      path.lineTo(curvePoint.dx, curvePoint.dy);
    }
    canvas.drawPath(path, paint);
  }

  _draw2OrderBezierCurves(Canvas canvas, Paint paint, List<Offset> points) {
    assert(points.length == 3);
    var path = Path();
    path.moveTo(points[0].dx, points[0].dy);
    for (var t = 1; t <= 100; t += 1) {
      var curvePoint = BezierUtil.get2OrderBezierPoint(
          points[0], points[1], points[2], t / 100.0);

      path.lineTo(curvePoint.dx, curvePoint.dy);
    }
    canvas.drawPath(path, paint);
  }
}
